package io.qifan.desktop.client.socket;

import io.qifan.desktop.api.auth.TokenUtil;
import io.qifan.desktop.api.protocol.CustomMessageConvert;
import io.qifan.desktop.api.protocol.response.BaseResponse;
import io.qifan.desktop.client.event.Events;
import io.qifan.desktop.client.store.device.DeviceStore;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;

import javax.annotation.PostConstruct;
import java.lang.reflect.Type;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Component
@Slf4j
public class SocketUtils {
    public static StompSession stompSession;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    private ThreadPoolTaskScheduler taskScheduler;

    @PostConstruct
    public void init() {
        taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.initialize();
        taskScheduler.scheduleWithFixedDelay(() -> {
            if (stompSession != null && !stompSession.isConnected()) {
                log.info("远程连接断开，正在尝试重连");
                eventPublisher.publishEvent(new Events.OfflineEvent());
                connect();
            }
        }, Duration.of(5, ChronoUnit.SECONDS));
    }

    @EventListener(Events.LoginSuccessEvent.class)
    public void handleLoginSuccessForWebsocket() {
        connect();
    }

    public void connect() {
        // 类似http的请求头
        WebSocketHttpHeaders webSocketHttpHeaders = new WebSocketHttpHeaders();
        webSocketHttpHeaders.add(TokenUtil.tokenName, TokenUtil.tokenValue);
        webSocketHttpHeaders.add("deviceId", String.valueOf(DeviceStore.currentDevice
                .getId()));
        StandardWebSocketClient socketClient = new StandardWebSocketClient();
        // 在基础的websocket连接上使用stomp协议（类似http协议）
        WebSocketStompClient stompClient = new WebSocketStompClient(socketClient);
        CompositeMessageConverter compositeMessageConverter = new CompositeMessageConverter(
                List.of(new CustomMessageConvert(), new MappingJackson2MessageConverter()));
        stompClient.setMessageConverter(compositeMessageConverter);
        // 心跳机制
        stompClient.setDefaultHeartbeat(new long[]{10000, 10000});
        stompClient.setTaskScheduler(this.taskScheduler);
        // websocket握手，这步是http请求，握手成功后保持tcp连接。
        stompClient.connect("ws://desktop-server:8080/handshake", webSocketHttpHeaders,
                new StompSessionHandlerAdapter() {
                    @Override
                    public void handleTransportError(StompSession session, Throwable exception) {
                        log.info("协议异常", exception);

                    }

                    @Override
                    public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
                        log.error("消息解析异常", exception);
                    }

                    @Override
                    public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
                        log.info("websocket连接成功：{}", session.getSessionId());
                        // 全局使用这个socket连接
                        stompSession = session;
                        eventPublisher.publishEvent(new Events.OnlineEvent());
                    }
                });
    }

    @EventListener(Events.OnlineEvent.class)
    public void subScribeResponse() {
        log.debug("订阅响应");
        stompSession.subscribe("/user/topic/desktop/response", new StompFrameHandler() {
            @Override
            public Type getPayloadType(StompHeaders headers) {
                return BaseResponse.class;
            }

            @Override
            public void handleFrame(StompHeaders headers, Object payload) {
                BaseResponse response = (BaseResponse) payload;
                if (response.getCode() != 0) {
                    throw new RuntimeException(response.getMsg());
                }
                eventPublisher.publishEvent(payload);
            }
        });
    }

    public static void send(String dest, Object msg) {
        stompSession.send(createHeader(dest), msg);
    }


    public static StompHeaders createHeader(String dest) {
        StompHeaders stompHeaders = new StompHeaders();
        stompHeaders.put(StompHeaders.DESTINATION, List.of(dest));
        stompHeaders.put(StompHeaders.CONTENT_TYPE, List.of(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE));
        return stompHeaders;
    }
}

